package com.mxx.common.conv;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.mxx.common.exception.JsonProcessException;
import com.mxx.common.utils.ClassUtil;
import com.sun.org.apache.regexp.internal.RE;

import java.io.IOException;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @Auther: Mr. Zeng
 * @Date: 2020/8/4 15:03
 * @Description: 暂无描述
 */
public class JacksonConverter implements ConvertValue {
    //    data parse exception
    private ObjectMapper objectMapper;

    private static ObjectMapper applicationMapper;

    public JacksonConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    public static JacksonConverter getInstance() {
        if (applicationMapper == null) {
            applicationMapper = new CustomObjectMapper();
        }
        return BeanUtil.singleton(JacksonConverter.class, new Object[]{applicationMapper});
    }

    public static JacksonConverter getInstance(ObjectMapper objectMapper) {
        return BeanUtil.singleton(JacksonConverter.class, new Object[]{objectMapper});
    }

    public <T> T convertValue(Object sourceValue, Class<T> targetClass) {
        if (sourceValue == null || sourceValue.getClass().equals(targetClass)) {
            return (T) sourceValue;
        }
        return objectMapper.convertValue(sourceValue, targetClass);
    }

    public <T> T convertValue(Object sourceValue, JavaType javaType) {
        if (sourceValue == null || sourceValue.getClass().equals(javaType.getRawClass())) {
            return (T) sourceValue;
        }
        return objectMapper.convertValue(sourceValue, javaType);
    }

    public String writeValueAsString(Object sourceValue) throws JsonProcessException {
        try {
            if (sourceValue == null) {
                return "";
            }
            if (sourceValue instanceof String) {
                return sourceValue.toString();
            }
            return objectMapper.writeValueAsString(sourceValue);
        } catch (JsonProcessingException ex) {
            throw new JsonProcessException(ex);
        }
    }

    public <T> T readValue(String content, Class<T> valueType) throws JsonProcessException {
        try {
            if (content == null || valueType.equals(String.class)) {
                return (T) content;
            }
            return objectMapper.readValue(content, valueType);
        } catch (JsonProcessingException ex) {
            throw new JsonProcessException(ex);
        }
    }

    public <T> T readValue(byte[] content, Class<T> valueType) throws JsonProcessException {
        try {
            return objectMapper.readValue(content, valueType);
        } catch (JsonProcessingException ex) {
            throw new JsonProcessException(ex);
        } catch (IOException ex) {
            throw new JsonProcessException(ex);
        }
    }

    public <T> T readValue(String content, JavaType valueType) throws JsonProcessException {
        try {
            if (content == null || valueType.getRawClass().equals(String.class)) {
                return (T) content;
            }
            return objectMapper.readValue(content, valueType);
        } catch (JsonProcessingException ex) {
            throw new JsonProcessException(ex);
        }
    }

    public <T> T readValue(byte[] content, JavaType valueType) throws JsonProcessException {
        try {
            return objectMapper.readValue(content, valueType);
        } catch (JsonProcessingException ex) {
            throw new JsonProcessException(ex);
        } catch (IOException ex) {
            throw new JsonProcessException(ex);
        }
    }

    public <T> List<T> readArrayListValue(String content, Class<T> classes) throws JsonProcessException {
        JavaType javaType = this.getListType(ArrayList.class, classes);
        return this.readValue(content, javaType);
    }

    public <T> T[] readArrayValue(String content, Class<T> classes) throws JsonProcessException {
        JavaType javaType = this.getArrayType(classes);
        return this.readValue(content, javaType);
    }

    public <T> T readArrayValue(String content, JavaType valType) throws JsonProcessException {
        JavaType javaType = this.getArrayType(valType);
        return this.readValue(content, javaType);
    }

    public <T> List<T> readArrayListValue(byte[] content, Class<T> classes) throws JsonProcessException {
        JavaType javaType = this.getListType(ArrayList.class, classes);
        return this.readValue(content, javaType);
    }

    public <T> List<T> readArrayListValue(String content, JavaType valType) throws JsonProcessException {
        JavaType javaType = this.getListType(ArrayList.class, valType);
        return this.readValue(content, javaType);
    }

    public <T> List<T> readArrayListValue(byte[] content, JavaType valType) throws JsonProcessException {
        JavaType javaType = this.getListType(ArrayList.class, valType);
        return this.readValue(content, javaType);
    }


    public <K, V> HashMap<K, V> readHashMapValue(String content, JavaType keyType, JavaType valType) throws JsonProcessException {
        JavaType javaType = this.getMapType(HashMap.class, keyType, valType);
        return this.readValue(content, javaType);
    }

    public <K, V> HashMap<K, V> readHashMapValue(byte[] content, JavaType keyType, JavaType valType) throws JsonProcessException {
        JavaType javaType = this.getMapType(HashMap.class, keyType, valType);
        return this.readValue(content, javaType);
    }

    public HashMap readHashMapValue(String content) throws JsonProcessException {
        JavaType javaType = this.getMapType();
        return this.readValue(content, javaType);
    }

    public HashMap readHashMapValue(byte[] content) throws JsonProcessException {
        JavaType javaType = this.getMapType();
        return this.readValue(content, javaType);
    }

    public TypeFactory getTypeFactory() {
        return objectMapper.getTypeFactory();
    }

    public JavaType getJavaType(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] types = parameterizedType.getActualTypeArguments();
            JavaType[] javaTypes = new JavaType[types.length];
            for (int i = 0; i < javaTypes.length; i++) {
                javaTypes[i] = this.getJavaType(types[i]);
            }
            return objectMapper.getTypeFactory().constructParametricType((Class<?>) parameterizedType.getRawType(), javaTypes);
        } else {
            return objectMapper.getTypeFactory().constructType(type);
        }
    }

    public MapType getMapType(Class<? extends Map> classes, Class keyClass, Class valClass) {
        return objectMapper.getTypeFactory().constructMapType(classes, keyClass, valClass);
    }

    public MapType getMapType(Class<? extends Map> classes, JavaType keyType, JavaType valType) {
        return objectMapper.getTypeFactory().constructMapType(classes, keyType, valType);
    }

    public MapType getMapType() {
        return objectMapper.getTypeFactory().constructRawMapType(HashMap.class);
    }

    public CollectionType getListType(Class<? extends Collection> classes, Class valClass) {
        return objectMapper.getTypeFactory().constructCollectionType(classes, valClass);
    }

    public CollectionType getListType(Class<? extends Collection> classes, JavaType valType) {
        return objectMapper.getTypeFactory().constructCollectionType(classes, valType);
    }

    public ObjectMapper getMapper() {
        return objectMapper;
    }

    public ArrayType getArrayType(JavaType valType) {
        return objectMapper.getTypeFactory().constructArrayType(valType);
    }

    public ArrayType getArrayType(Class valClass) {
        return objectMapper.getTypeFactory().constructArrayType(valClass);
    }


    public JavaType getParameterType(Parameter parameter) {
        Class paramClasses = parameter.getType();
        JavaType parameterType;
        TypeFactory typeFactory = objectMapper.getTypeFactory();
        if (ClassUtil.isSuperInterface(parameter.getType(), Map.class)) {
            Type type = parameter.getParameterizedType();
            if (type instanceof Class) {
                JavaType keyType = typeFactory.constructType(String.class);
                JavaType valType = typeFactory.constructType(Object.class);
                parameterType = typeFactory.constructMapType(paramClasses, keyType, valType);
            } else {
                Type[] types = ((ParameterizedType) type).getActualTypeArguments();
                JavaType keyType = typeFactory.constructType(types[0]);
                JavaType valType = typeFactory.constructType(types[1]);
                parameterType = typeFactory.constructMapType(paramClasses, keyType, valType);
            }
        } else if (ClassUtil.isSuperInterface(parameter.getType(), Collection.class)) {
            Type type = parameter.getParameterizedType();
            if (type instanceof Class) {
                parameterType = typeFactory.constructRawCollectionType((Class<? extends Collection>) parameter.getType());
            } else {
                Type[] types = ((ParameterizedType) type).getActualTypeArguments();
                JavaType valType = typeFactory.constructType(types[0]);
                parameterType = typeFactory.constructCollectionType((Class<? extends Collection>) parameter.getType(), valType);
            }
        } else {
            parameterType = typeFactory.constructType(paramClasses);
        }
        return parameterType;
    }

}
